package com.honey.compilation.sql;

import java.sql.Types;

import com.honey.core.builder.CharacterBuilder;
import com.honey.core.builder.CharacterBuilderFactory;
import com.honey.core.compiler.CompilationElement;
import com.honey.core.compiler.IndentSpace;

/**
 * SQL 92 中对数据库定义如下:
 * 
 * &lt;data type&gt; ::= <br />
 * &lt;character string type&gt; [ CHARACTER SET &lt;character set
 * specification&gt; ]<br /> | &lt;national character string type&gt;<br /> |
 * &lt;bit string type&gt;<br /> | &lt;numeric type&gt;<br /> | &lt;datetime
 * type&gt;<br /> | &lt;interval type&gt;<br />
 * <br />
 * &lt;character string type&gt; ::= <br />
 * CHARACTER [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * CHAR [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * CHARACTER VARYING [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * CHAR VARYING [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * VARCHAR [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br />
 * <br />
 * &lt;national character string type&gt; ::= <br />
 * NATIONAL CHARACTER [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * NATIONAL CHAR [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * NCHAR [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> |
 * NATIONAL CHARACTER VARYING [ &lt;left paren&gt; &lt;length&gt; &lt;right
 * paren&gt; ]<br /> | NATIONAL CHAR VARYING [ &lt;left paren&gt;
 * &lt;length&gt; &lt;right paren&gt; ]<br /> | NCHAR VARYING [ &lt;left
 * paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br />
 * <br />
 * &lt;bit string type&gt; ::= <br />
 * BIT [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br /> | BIT
 * VARYING [ &lt;left paren&gt; &lt;length&gt; &lt;right paren&gt; ]<br />
 * <br />
 * 
 * &lt;numeric type&gt; ::= <br />
 * &lt;exact numeric type&gt;<br /> | &lt;approximate numeric type&gt;<br />
 * <br />
 * &lt;exact numeric type&gt; ::= <br />
 * NUMERIC [ &lt;left paren&gt; &lt;precision&gt; [ &lt;comma&gt; &lt;scale&gt; ]
 * &lt;right paren&gt; ]<br /> | DECIMAL [ &lt;left paren&gt; &lt;precision&gt; [
 * &lt;comma&gt; &lt;scale&gt; ] &lt;right paren&gt; ]<br /> | DEC [ &lt;left
 * paren&gt; &lt;precision&gt; [ &lt;comma&gt; &lt;scale&gt; ] &lt;right
 * paren&gt; ]<br /> | INTEGER <br /> | INT <br /> | SMALLINT<br />
 * <br />
 * &lt;approximate numeric type&gt; ::= <br />
 * FLOAT [ &lt;left paren&gt; &lt;precision &lt;right paren&gt; ]<br /> | REAL
 * <br /> | DOUBLE PRECISION<br />
 * <br />
 * &lt;datetime type&gt; ::= <br />
 * DATE <br /> | TIME [ &lt;left paren&gt; &lt;time precision&gt; &lt;right
 * paren&gt; ] [ WITH TIME ZONE ]<br /> | TIMESTAMP [ &lt;left paren&gt;
 * &lt;timestamp precision&gt; &lt;right paren&gt; ] [ WITH TIME ZONE ]<br />
 * <br />
 * 
 * <p>
 * &lt;interval type&gt; ::= INTERVAL &lt;interval qualifier&gt;
 * </p>
 * <p>
 * &lt;interval qualifier&gt; ::= <br />
 * &lt;start field&gt; TO &lt;end field&gt;<br /> | &lt;single datetime
 * field&gt;
 * </p>
 * <br />
 * <br />
 * 
 * @author Administrator
 * 
 */
public class SqlType implements CompilationElement{
	
	/**
	 * 长度 ,SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度
	 */
	private int length;

	/**
	 * 小数位(只有浮点数支持)
	 */
	private int scale;
	
	/**
	 * 无符号数(只有数字类型支持)
	 */
	private boolean unsigned;
	
	private ST st; 
	
	/**
	 * 构造函数
	 * @param jdbctype jdk中规定的数据库类型(java.sql.Types)
	 */
	public SqlType(int jdbctype) {
		this(jdbctype, -1 , -1, false);
	}

	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param length 长度, SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度
	 */
	public SqlType(int jdbctype, int length) {
		this(jdbctype, length, -1, false);
	}
	
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param unsigned 是否是无符号类型(SQL 92中没有定义无符号类型)
	 */
	public SqlType(int jdbctype, boolean unsigned) {
		this(jdbctype, -1 , -1, unsigned);
	}
	
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param length 长度,SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度 
	 * @param unsigned  是否是无符号类型(SQL 92中没有定义无符号类型)
	 */
	public SqlType(int jdbctype, int length, boolean unsigned) {
		this(jdbctype, length, -1, unsigned);
	}
	
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param length 长度,SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度
	 * @param scale 小数位
	 * @param unsigned 是否是无符号类型(SQL 92中没有定义无符号类型)
	 */
	public SqlType(int jdbctype, int length,int scale, boolean unsigned) {
		st = ST.getST(jdbctype,unsigned);
		this.length = length;
		this.scale = scale;
		this.unsigned = unsigned;
		if(this.length<1 ){
			this.length = st.getDefaultLength();
		}
		if(this.scale<1 ){
			this.scale = st.getDefaultscale();
		}
	}
	
	/**
	 * 
	 * jdk中规定的数据库类型(java.sql.Types
	 * @param jdbctype
	 * @return
	 */
	public static SqlType getInstant(int jdbctype){
		return new  SqlType(jdbctype) ;
	}
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param length 长度, SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度
	 */
	public static SqlType getInstant(int jdbctype, int length) {
		return new SqlType(jdbctype, length);
	}
	
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param unsigned 是否是无符号类型(SQL 92中没有定义无符号类型)
	 */
	public static SqlType getInstant(int jdbctype, boolean unsigned) {
		return new SqlType(jdbctype, unsigned);
	}
	
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param length 长度,SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度 
	 * @param unsigned  是否是无符号类型(SQL 92中没有定义无符号类型)
	 */
	public static SqlType getInstant(int jdbctype, int length, boolean unsigned) {
		return new SqlType(jdbctype, length,unsigned);
	}
	
	/**
	 * 构造函数
	 * @param jdbctype jdbctype jdk中规定的数据库类型(java.sql.Types)
	 * @param length 长度,SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度
	 * @param scale 小数位
	 * @param unsigned 是否是无符号类型(SQL 92中没有定义无符号类型)
	 */
	public static SqlType getInstant(int jdbctype, int length,int scale, boolean unsigned) {
		return new SqlType(jdbctype,length,scale,unsigned);
	}
	
	/**
	 * 获取数据库类型
	 * @return 返回java.sql.Types中定义的类型
	 */
	public int getJdbctype(){
		return st.jdbctype;
	}

	/**
	 * 获取长度,SQL 92中只有部分(例如DECIMAL,NUMERIC )定义长度,其它都没有定义长度
	 * @return
	 */
	public int getLength() {
		return length;
	}
	
	/**
	 * 获取小数位
	 * @return
	 */
	public int getScale() {
		return scale;
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.honey.core.dom.CompilationElement#compiledContent(int)
	 */
	@Override
	public CharacterBuilder compiledContent(int indentLevel) {
		CharacterBuilder answer = CharacterBuilderFactory.createC16StringBuilder();
		IndentSpace.spaceIndent(answer, indentLevel);
		answer.append(st.getDbName());
		if(this.length>0 && st.defaultLength >0 ){
			answer.append(SqlKeyWord.OPERATION_PAREN_LEFT);
			answer.append(this.length);
			if(this.scale>0 && st.defaultscale >0  ){
				answer.append(SqlKeyWord.OPERATION_COMMA).append(this.scale);
			}
			answer.append(SqlKeyWord.OPERATION_PAREN_RIGHT);
		}
		answer.append(SqlKeyWord.OPERATION_SPACE);
		return answer;
	}
	
	
	/**
	 * java.sql.Types中的类型到SQL 92类型映射
	 * 
	 * @author Administrator
	 *
	 */
	private enum  ST{
 
		CHARACTER(Types.CHAR,"CHARACTER" ,255, -1),
		CHAR(Types.CHAR, "CHAR", 255, -1),
		CHARACTER_VARYING(Types.CHAR, "CHAR" , 255, -1),
		CHAR_VARYING(Types.CHAR, "CHAR" , 255, -1),
		VARCHAR(Types.VARCHAR, "VARCHAR" , 255, -1),
		
		NATIONAL_CHARACTER(Types.NCHAR, "NATIONAL CHARACTER", 255, -1),
		NATIONAL_CHAR(Types.NCHAR, "NATIONAL CHAR", 255, -1),
		NCHAR(Types.NCHAR, "NCHAR", 255, -1),
		NATIONAL_CHARACTER_VARYING(Types.NVARCHAR, "NATIONAL CHARACTER VARYING", 255,-1),
		NATIONAL_CHAR_VARYING(Types.NVARCHAR, "NATIONAL CHAR VARYING" , 255, -1),
		NCHAR_VARYING(Types.NVARCHAR, "NCHAR VARYING", 255, -1),
		
		BIT(Types.BIT, "BIT", 1, -1),
		BIT_VARYING(Types.BIT, "BIT", 1, -1),
		
		NUMERIC(Types.NUMERIC, "NUMERIC", 10, 3 ), 
		DECIMAL(Types.DECIMAL,  "DECIMAL", 10, 3), 
		DEC(Types.DECIMAL,  "NUMERIC", 10, 3 ), 
		INTEGER(Types.INTEGER,  "INTEGER", -1,-1), 
		INT(Types.INTEGER,  "INT", -1,-1), 
		SMALLINT(Types.SMALLINT,  "SMALLINT", -1,-1), 
		FLOAT(Types.FLOAT,  "FLOAT", -1,-1),
		REAL (Types.REAL ,  "REAL", -1,-1),
		DOUBLE_PRECISION (Types.DOUBLE ,  "DOUBLE PRECISION", -1,-1),
		
		
		DATE(Types.DATE ,  "DATE", -1,-1),
		TIME(Types.TIME ,  "TIME", -1,-1),
		TIMESTAMP(Types.TIMESTAMP ,  "TIMESTAMP", -1,-1),
		
		
		
		// 扩充 SQL 92中没有定义,但是很多数据库都支持的类型.
		BIGINT(Types.BIGINT, "BIGINT" ,-1 ,-1),
		CLOB(Types.CLOB, "CLOB", -1,-1), 
		BLOB(Types.BLOB, "BLOB", -1 ,-1), 
		TINYINT(Types.TINYINT, "TINYINT" ,-1,-1),
		DOUBLE(Types.DOUBLE, "DOUBLE PRECISION", -1,-1 ), 
		
		BIGINTUNSIGNED(Types.BIGINT, "BIGINT UNSIGNED" ,-1 ,-1),
		INTEGERUNSIGNED(Types.INTEGER, "INT UNSIGNED", -1,-1 ),
		DOUBLEUNSIGNED(Types.DOUBLE, "DOUBLE UNSIGNED", -1,-1),
		SMALLINTUNSIGNED(Types.SMALLINT, "SMALLINT UNSIGNED",-1,-1),
		REALUNSIGNED(Types.REAL, "REAL UNSIGNED", -1,-1 ),
		TINYINTUNSIGNED(Types.TINYINT, "TINYINT UNSIGNED" ,-1,-1),
		DECIMALUNSIGNED(Types.DECIMAL,  "DECIMAL UNSIGNED", 10,3), 
		NUMERICUNSIGNED(Types.NUMERIC, "NUMERIC UNSIGNED", 10,3),
		FLOATUNSIGNED(Types.FLOAT, "FLOAT UNSIGNED", -1,-1 ),
		
		;
			
		
/*
 * ARRAY(Types.ARRAY, "ARRAY", 255, -1,false,true), BINARY(Types.BINARY,
 * "BINARY", 255,-1,false,true),
 * 
 * BIGINT(Types.BIGINT, "BIGINT" ,20 ,-1,false,true),
 * BIGINTUNSIGNED(Types.BIGINT, "BIGINT UNSIGNED" ,20 ,-1,false,true),
 * 
 * BIT(Types.BIT, "BIT",1 ,-1,false,true), BLOB(Types.BLOB, "BLOB", -1
 * ,-1,false,true), BOOLEAN(Types.BOOLEAN, "BOOLEAN" ,-1 ,-1,false,true),
 * CHAR(Types.CHAR, "CHAR",255,-1,false,false), CLOB(Types.CLOB, "CLOB",
 * -1,-1,false,true), DATALINK(Types.DATALINK, "DATALINK", -1,-1,false ,true),
 * DATE(Types.DATE, "DATE", -1,-1 ,false,true),
 * 
 * DECIMAL(Types.DECIMAL, "DECIMAL", 10,-1 ,true,false),
 * DECIMALUNSIGNED(Types.DECIMAL, "DECIMAL UNSIGNED", 10,-1 ,true ,false),
 * 
 * DISTINCT(Types.DISTINCT, "DISTINCT", -1,-1,false ,true),
 * 
 * DOUBLE(Types.DOUBLE, "DOUBLE", -1,-1,true ,true),
 * DOUBLEUNSIGNED(Types.DOUBLE, "DOUBLE UNSIGNED", -1,-1,true ,true),
 * 
 * FLOAT(Types.FLOAT, "FLOAT", -1,-1,true ,true), FLOATUNSIGNED(Types.FLOAT,
 * "FLOAT UNSIGNED", -1,-1,true ,true),
 * 
 * INTEGER(Types.INTEGER, "INT", 11,-1 ,false ,true),
 * INTEGERUNSIGNED(Types.INTEGER, "INT UNSIGNED", 11,-1 ,false,true),
 * 
 * JAVA_OBJECT(Types.JAVA_OBJECT, "JAVA_OBJECT", -1,-1 ,false,true ),
 * LONGVARBINARY(Types.LONGVARBINARY, "LONGVARBINARY" , -1,-1 ,false,true),
 * LONGVARCHAR(Types.LONGVARCHAR, "LONGVARCHAR" , -1,-1,false ,true),
 * NULL(Types.NULL, "NULL", -1,-1 ,false,true), NUMERIC(Types.NUMERIC,
 * "NUMERIC", 10,-1 ,true,false), NUMERICUNSIGNED(Types.NUMERIC, "NUMERIC
 * UNSIGNED", 10,-1 ,true,false), REAL(Types.REAL, "REAL", -1,-1 ,true,true),
 * REALUNSIGNED(Types.REAL, "REAL UNSIGNED", -1,-1 ,true,true),
 * 
 * REF(Types.REF, "REF",-1,-1 ,false,true), SMALLINT(Types.SMALLINT, "SMALLINT"
 * ,6,-1,false,true), SMALLINTUNSIGNED(Types.SMALLINT, "SMALLINT
 * UNSIGNED",6,-1,false,true),
 * 
 * 
 * STRUCT(Types.STRUCT, "STRUCT",-1,-1,false,true), TIME(Types.TIME, "TIME"
 * ,-1,-1,false,true), TIMESTAMP(Types.TIMESTAMP, "TIMESTAMP"
 * ,-1,-1,false,true), TINYINT(Types.TINYINT, "TINYINT" ,4,-1,false,true),
 * TINYINTUNSIGNED(Types.TINYINT, "TINYINT UNSIGNED" ,4,-1,false,true),
 * 
 * VARBINARY(Types.VARBINARY, "VARBINARY" ,-1,-1,false,false),
 * VARCHAR(Types.VARCHAR, "VARCHAR" ,255,-1,false,false)
 */
		;

		/**
		 * jdbc 类型
		 */
		private final int jdbctype;

		/**
		 * 数据库类型名称
		 */
		private final String dbName;

		/**
		 * 默认长度(小于等于0表示由数据库决定长度)
		 */
		private final int defaultLength ;
		
		/**
		 * 默认的浮点数小数位长度 (小于等于0表示由数据库决定小数长度)
		 */
		private final int defaultscale ;
		
		
		/**
		 * 
		 * @param jdbctype
		 * @param dbName
		 *            数据库名称
		 * @param defaultLength
		 *            默认长度
		 * @param defaultscale
		 *            小数长度
		 * @param supportScale
		 *            是否支持小数长度
		 */
		private ST(int jdbctype, String dbName,int defaultLength ,int defaultscale ) {
			this.jdbctype = jdbctype;
			this.dbName = dbName;
			this.defaultLength= defaultLength;
			this.defaultscale= defaultscale;
		}
		
		public static ST getST( int jdbctype,boolean unsigned ){
			ST answer=null;
			switch(jdbctype){
				// case Types.ARRAY : answer = null ; break ;
				// case Types.BINARY : answer = null ; break ;
				case Types.BIGINT : answer = unsigned ? ST.BIGINTUNSIGNED : ST.BIGINT ; break ;
				case Types.BIT : answer = ST.BIT ; break ;
				case Types.BLOB : answer = ST.BLOB ; break ;
				case Types.BOOLEAN : answer = ST.BIT ; break ;
				case Types.CHAR : answer = ST.CHAR ; break ;
				case Types.CLOB : answer = ST.CLOB ; break ;
				case Types.DATALINK : answer = ST.DATE ; break ;
				case Types.DATE : answer = ST.DATE ; break ;
				case Types.DECIMAL : answer =unsigned?  ST.DECIMALUNSIGNED : ST.DECIMAL ; break ;
				// case Types.DISTINCT : answer = null ; break ;
				case Types.DOUBLE : answer = unsigned? ST.DOUBLEUNSIGNED : ST.DOUBLE ; break ;
				case Types.FLOAT : answer = unsigned?  ST.FLOATUNSIGNED : ST.FLOAT ; break ;
				case Types.INTEGER : answer =unsigned? ST.INTEGERUNSIGNED : ST.INTEGER ; break ;
				// case Types.JAVA_OBJECT : answer = null ; break ;
				// case Types.LONGVARBINARY : answer = null; break ;
				// case Types.LONGVARCHAR : answer = null ; break ;
				// case Types.NULL : answer = null ; break ;
				case Types.NUMERIC : answer =unsigned? ST.NUMERICUNSIGNED : ST.NUMERIC ; break ;
				case Types.REAL : answer =unsigned? ST.REALUNSIGNED : ST.REAL ; break ;
				// case Types.REF : answer = null ; break ;
				case Types.SMALLINT : answer =unsigned? ST.SMALLINTUNSIGNED : ST.SMALLINT ; break ;
				// case Types.STRUCT : answer = null; break ;
				case Types.TIME : answer = ST.TIME ; break ;
				case Types.TIMESTAMP : answer = ST.TIMESTAMP ; break ;
				case Types.TINYINT : answer =unsigned? ST.TINYINTUNSIGNED : ST.TINYINT ; break ;
				// case Types.VARBINARY : answer = ST.VARBINARY ; break ;
				case Types.VARCHAR : answer = ST.VARCHAR ; break ;
				case Types.NCHAR : answer = ST.NCHAR ; break ;
				
				
				default : answer = ST.VARCHAR ; break ;
			}
			return answer;
		}
		public int getJdbctype() {
			return jdbctype;
		}
		public String getDbName() {
			return dbName;
		}
		public int getDefaultLength() {
			return defaultLength;
		}
		public int getDefaultscale() {
			return defaultscale;
		}		
	}
}
